package handler

import (
	"context"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"

	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/database"
	"kop/modules/treasure/configuration"
	"kop/modules/treasure/configuration/buff"
	pointConf "kop/modules/treasure/configuration/point"
	"kop/modules/treasure/consts"
	"kop/modules/treasure/models"
	"kop/pb"
	"kop/pb/service"
	"kop/util/rand"

	battleM "kop/modules/battle/models"
	itemC "kop/modules/item/client"
	cellM "kop/modules/treasure/models/cell"
)

type TreasureServiceHandler struct {
	context2.Context
}

func (h *TreasureServiceHandler) GetGrades(ctx context.Context, req *service.RoleRequest, resp *service.TreasureGrades) error {

	resp.Grades = models.GetGrades(database.DB, req.RoleID)

	for _, conf := range configuration.TreasureConfigs {

		var find = false
		for _, v := range resp.Grades {
			if v.MapID == conf.MapID {
				find = true
			}
		}

		if !find {
			resp.Grades = append(resp.Grades, &pb.TreasureGrade{MapID: conf.MapID, Grade: 0})
		}
	}

	return nil
}

func (h *TreasureServiceHandler) GetGrade(ctx context.Context, req *service.DefaultRequest, point *pb.TreasureGrade) error {

	var _, err = configuration.GetTreasureByMapID(req.Value)
	if err != nil {
		return err
	}

	*point = *models.GetGrade(database.DB, req.RoleID, req.Value)
	return nil
}

func (h *TreasureServiceHandler) GetTreasure(ctx context.Context, req *service.RoleRequest, treasure *pb.Treasure) error {

	*treasure = models.GetTreasure(database.DB, req.RoleID)
	return nil
}

// 必填字段 RoleID, MapID
func (h *TreasureServiceHandler) Init(ctx context.Context, req *service.TreasureInitRequest, treasure *pb.Treasure) error {

	*treasure = models.GetTreasure(database.DB, req.RoleID)

	treasure.PlayNum -= 1
	if treasure.PlayNum < 0 {
		return errors.ParamError
	}

	var conf, err = configuration.GetTreasureByMapID(req.MapID)
	if err != nil {
		return err
	}

	treasure.RoleID = req.RoleID
	treasure.MapID = conf.MapID //
	treasure.Number = consts.MaxNumber
	treasure.Point1 = 0
	treasure.Point2 = 0
	treasure.Point3 = 0
	treasure.Point4 = 0
	treasure.Buff1 = 0
	treasure.Buff2 = 0
	treasure.Buff3 = 0
	treasure.AddPoint = 0
	treasure.BuffOptions = []int32{}
	treasure.Cells = make([]*pb.Cell, 0, consts.X*consts.Y)
	//treasure.Captains = req.Captains

	for y := int32(1); y <= consts.Y; y++ {
		for x := int32(1); x <= consts.X; x++ {

			var cell = pb.Cell{
				X:       x,
				Y:       y,
				State:   pb.TCellState_Unknown,
				Type:    pb.TCellType_Empty,
				Npc:     nil,
				AwardID: 0,
			}
			// 初始位置 3.3
			if x == 3 && y == 3 {
				cell.State = pb.TCellState_Open
			} else {
				// 默认积分=10
				cell.Point = configuration.GetCellPoint(pb.TCellType_Empty)
			}

			treasure.Cells = append(treasure.Cells, &cell)
		}
	}

	for _, cellConf := range configuration.CellConfigs {

		switch cellConf.ID {
		case 1:
			//小宝箱
			var n = int(rand.Int32(cellConf.CountMin, cellConf.CountMax))
			randBox(treasure.Cells, n, cellConf.Score, conf.BoxAwardID[0])
		case 2:
			//中宝箱
			var n = int(rand.Int32(cellConf.CountMin, cellConf.CountMax))
			randBox(treasure.Cells, n, cellConf.Score, conf.BoxAwardID[1])
		case 3:
			//大宝箱
			var n = int(rand.Int32(cellConf.CountMin, cellConf.CountMax))
			randBox(treasure.Cells, n, cellConf.Score, conf.BoxAwardID[2])
		case 4:
			// 拦截船
			for i := 0; i < len(conf.EliteID); i++ {

				var arr = make([]*pb.Cell, 0)
				for _, v := range treasure.Cells {
					if v.State == pb.TCellState_Unknown && v.Type == pb.TCellType_Empty {
						arr = append(arr, v)
					}
				}

				if len(arr) > 0 {
					var index = rand.Int(0, len(arr)-1)
					arr[index].Type = pb.TCellType_Npc
					arr[index].Point = configuration.GetCellPoint(pb.TCellType_Npc)
					arr[index].AwardID = conf.EliteAwardID
					arr[index].Npc, err = battleM.GetNpc(conf.EliteID[i], conf.EliteLevel)
					if err != nil {
						return err
					}
				}
			}

		case 5:
			// Boss
			var arr = make([]*pb.Cell, 0)
			for _, v := range treasure.Cells {
				if v.State == pb.TCellState_Unknown && v.Type == pb.TCellType_Empty {
					arr = append(arr, v)
				}
			}

			if len(arr) > 0 {
				var index = rand.Int(0, len(arr)-1)
				arr[index].Type = pb.TCellType_Boss
				arr[index].Point = configuration.GetCellPoint(pb.TCellType_Boss)
				arr[index].AwardID = conf.BossAwardID
				arr[index].Npc, err = battleM.GetNpc(conf.BossID, conf.BossLevel)
				if err != nil {
					return err
				}
			}
		}
	}

	models.SaveTreasure(database.DB, treasure)
	return nil
}

func (h *TreasureServiceHandler) Save(ctx context.Context, req *pb.Treasure, treasure *pb.Treasure) error {

	*treasure = *req

	models.SaveTreasure(database.DB, treasure)
	return nil
}

func (h *TreasureServiceHandler) Box(ctx context.Context, req *service.ExploreRequest, treasure *pb.Treasure) error {

	*treasure = models.GetTreasure(database.DB, req.RoleID)
	if treasure.MapID < 1 {
		return errors.ParamError
	}

	var cell = cellM.Cells(treasure.Cells).GetCell(req.X, req.Y)

	if cell.Type != pb.TCellType_Box {
		return errors.ParamError
	}

	treasure.AddPoint = cell.Point

	if treasure.Buff1 == 7 || treasure.Buff2 == 7 || treasure.Buff3 == 7 {
		// 增加宝箱积分Buff
		var buffConf, _ = buff.GetByBuffID(7)
		treasure.AddPoint = int32(float64(treasure.AddPoint) * (1 + buffConf.Value))
	}

	treasure.Point2 += treasure.AddPoint
	checkBuff(treasure)

	cell.State = pb.TCellState_Open
	cell.Type = pb.TCellType_Empty

	models.SaveTreasure(database.DB, treasure)
	return nil
}

func (h *TreasureServiceHandler) Explore(ctx context.Context, req *service.ExploreRequest, treasure *pb.Treasure) error {

	*treasure = models.GetTreasure(database.DB, req.RoleID)
	if treasure.MapID < 1 {
		return errors.ParamError
	}

	treasure.Number -= 1
	if treasure.Number < 0 {
		return errors.ParamError
	}

	var cell = cellM.Cells(treasure.Cells).GetCell(req.X, req.Y)
	if cell.State != pb.TCellState_Unknown {
		return errors.New("只能探索未知区域")
	}

	var find = false

	var xBegin = req.X - 1
	var xEnd = req.X + 1

	var yBegin = req.Y - 1
	var yEnd = req.Y + 1

	for x := xBegin; x <= xEnd; x++ {
		for y := yBegin; y <= yEnd; y++ {
			for _, cell := range treasure.Cells {
				if cell.X == x && cell.Y == y && cell.State == pb.TCellState_Open {

					find = true
				}
			}
		}
	}

	if !find {
		return errors.New("不能穿透探索")
	}

	if cell.Type == pb.TCellType_Empty {
		// 探索成功加空格子的积分
		treasure.AddPoint = cell.Point
		treasure.Point1 += treasure.AddPoint
		checkBuff(treasure)
	}

	cell.State = pb.TCellState_Open
	if cell.Type == pb.TCellType_Npc || cell.Type == pb.TCellType_Boss {
		// 周围8个格子锁定
		var xBegin = req.X - 1
		var xEnd = req.X + 1

		var yBegin = req.Y - 1
		var yEnd = req.Y + 1

		for x := xBegin; x <= xEnd; x++ {
			for y := yBegin; y <= yEnd; y++ {
				for _, cell := range treasure.Cells {
					if cell.X == x && cell.Y == y && cell.State == pb.TCellState_Unknown {

						cell.State = pb.TCellState_Lock
					}
				}
			}
		}
	}

	models.SaveTreasure(database.DB, treasure)
	return nil
}

func (h *TreasureServiceHandler) NPCWin(ctx context.Context, req *service.ExploreRequest, treasure *pb.Treasure) error {

	*treasure = models.GetTreasure(database.DB, req.RoleID)

	var cell = cellM.Cells(treasure.Cells).GetCell(req.X, req.Y)

	treasure.AddPoint = cell.Point
	treasure.Point3 += treasure.AddPoint
	checkBuff(treasure)

	if cell.Type == pb.TCellType_Npc {
		var xBegin = req.X - 1
		var xEnd = req.X + 1

		var yBegin = req.Y - 1
		var yEnd = req.Y + 1

		for x := xBegin; x <= xEnd; x++ {
			for y := yBegin; y <= yEnd; y++ {
				for _, cell := range treasure.Cells {
					if cell.X == x && cell.Y == y && cell.State == pb.TCellState_Lock {

						cell.State = pb.TCellState_Unknown
					}
				}
			}
		}
	}

	cell.State = pb.TCellState_Open
	cell.Type = pb.TCellType_Empty
	cell.Npc = nil

	models.SaveTreasure(database.DB, treasure)
	return nil
}
func (h *TreasureServiceHandler) BossWin(ctx context.Context, req *service.ExploreRequest, treasure *pb.Treasure) error {

	*treasure = models.GetTreasure(database.DB, req.RoleID)

	var conf, err = configuration.GetTreasureByMapID(treasure.MapID)
	if err != nil {
		return err
	}

	var cell = cellM.Cells(treasure.Cells).GetCell(req.X, req.Y)
	cell.State = pb.TCellState_Open
	cell.Type = pb.TCellType_Cup
	cell.Npc = nil
	cell.AwardID = conf.AwardID

	treasure.AddPoint = cell.Point
	treasure.Point4 += treasure.AddPoint
	checkBuff(treasure)

	var xBegin = req.X - 1
	var xEnd = req.X + 1

	var yBegin = req.Y - 1
	var yEnd = req.Y + 1

	for x := xBegin; x <= xEnd; x++ {
		for y := yBegin; y <= yEnd; y++ {
			for _, cell := range treasure.Cells {
				if cell.X == x && cell.Y == y && cell.State == pb.TCellState_Lock {

					cell.State = pb.TCellState_Unknown
				}
			}
		}
	}

	models.SaveTreasure(database.DB, treasure)
	return nil
}

func (h *TreasureServiceHandler) Finish(ctx context.Context, req *pb.Treasure, resp *service.TreasureFinishResponse) (err error) {

	var session = database.DB.Begin()
	if session.Error != nil {
		return session.Error
	}

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()
		}
	}()

	resp.Grade = models.GetGrade(session, req.RoleID, req.MapID)
	resp.Treasure = req

	req.MapID = 0

	conf, err := pointConf.GetByPoint(getPoint(req))
	if err != nil {
		panic(err)
	}

	if conf.Grade > resp.Grade.Grade {

		resp.Grade.Grade = conf.Grade
		models.SaveGrade(session, req.RoleID, resp.Grade)
	}

	models.SaveTreasure(session, req)
	return session.Commit().Error
}

func (h *TreasureServiceHandler) Buff(ctx context.Context, req *service.TreasureSBuffRequest, treasure *pb.Treasure) error {

	*treasure = *req.Treasure
	var buffID = treasure.BuffOptions[req.Index]

	if treasure.Buff1 == 0 {
		treasure.Buff1 = buffID
	} else if treasure.Buff2 == 0 {
		treasure.Buff2 = buffID
	} else {
		treasure.Buff3 = buffID
	}

	//1	自由阵营船长提升智力20%	战斗增益
	//2	正义阵营船长提升智力20%	战斗增益
	//3	中立阵营船长提升智力20%	战斗增益
	//4	自由阵营船长提升勇气20%	战斗增益
	//5	正义阵营船长提升勇气20%	战斗增益
	//6	中立阵营船长提升勇气20%	战斗增益

	var conf, err = buff.GetByBuffID(buffID)
	if err != nil {
		panic(err)
	}

	if conf.BuffType == 9 {
		// 补充探索次数
		treasure.Number += int32(conf.Value)
	}

	treasure.BuffOptions = []int32{}
	models.SaveTreasure(database.DB, treasure)
	return nil
}

func (h *TreasureServiceHandler) AddNum(ctx context.Context, _ *empty.Empty, resp *wrappers.Int32Value) error {

	// 101321 夺宝地图
	if err := itemC.AsyncDelItem(h.GetRoleID(ctx), h.GetServerID(ctx), 101321, 1); err != nil {
		return err
	}

	var treasure = models.GetTreasure(database.DB, h.GetRoleID(ctx))
	treasure.PlayNum += 1
	models.SaveTreasure(database.DB, &treasure)

	resp.Value = treasure.PlayNum
	return nil
}

func randBox(cells []*pb.Cell, n int, point, awardID int32) {

	for i := 0; i < n; i++ {

		var arr = make([]*pb.Cell, 0)
		for _, v := range cells {
			if v.State == pb.TCellState_Unknown && v.Type == pb.TCellType_Empty {
				arr = append(arr, v)
			}
		}

		if len(arr) < 1 {
			break
		}

		var index = rand.Int(0, len(arr)-1)
		arr[index].Type = pb.TCellType_Box
		arr[index].Point = point
		arr[index].AwardID = awardID
	}
}

// 随机3个BUFF选项
func randBuffs(treasure *pb.Treasure) {

	var buffs = make([]rand.Value, 0, len(buff.Configs))
	for _, v := range buff.Configs {
		if v.ID == treasure.Buff1 || v.ID == treasure.Buff2 || v.ID == treasure.Buff3 {
			continue
		}
		// 注意迭代器用的同一内存地址 直接 &v 值会都一样
		var conf = v
		buffs = append(buffs, &conf)
	}

	var res = rand.WithWeight(buffs, 3)
	treasure.BuffOptions = []int32{res[0].GetID(), res[1].GetID(), res[2].GetID()}
}

func getPoint(treasure *pb.Treasure) int32 {
	return treasure.Point1 + treasure.Point2 + treasure.Point3 + treasure.Point4
}

func checkBuff(treasure *pb.Treasure) {

	if len(treasure.BuffOptions) != 0 {
		return
	}

	var point = getPoint(treasure)

	//112（25%）、225（50%）、337（75%）
	if point-treasure.AddPoint < 112 && point >= 112 {
		// 第一个BUFF奖励
		randBuffs(treasure)

	} else if point-treasure.AddPoint < 225 && point >= 225 {
		// 第二个BUFF奖励
		randBuffs(treasure)

	} else if point-treasure.AddPoint < 225 && point >= 337 {
		// 第三个BUFF奖励
		randBuffs(treasure)
	}
}
